package io.bootique.config.jackson; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Optional; import java.util.stream.StreamSupport; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; /** * A helper class to navigate {@link JsonNode} objects. */ class PathSegment implements Iterable<PathSegment> { private String remainingPath; private String incomingPath; private JsonNode node; private PathSegment parent; protected char pathSeparator; PathSegment(JsonNode node, String remainingPath, char pathSeparator) { this(node, null, null, remainingPath, pathSeparator); } protected PathSegment(JsonNode node, PathSegment parent, String incomingPath, String remainingPath, char pathSeparator) { this.node = node; this.parent = parent; this.incomingPath = incomingPath; this.pathSeparator = pathSeparator; if (remainingPath != null && remainingPath.length() > 0 && remainingPath.charAt(remainingPath.length() - 1) == pathSeparator) { remainingPath = remainingPath.substring(0, remainingPath.length() - 1); } this.remainingPath = remainingPath; } public Optional<PathSegment> lastPathComponent() { return StreamSupport.stream(spliterator(), false).reduce((a, b) -> b); } public JsonNode getNode() { return node; } public JsonNode getParentNode() { return parent.getNode(); } public String getIncomingPath() { return incomingPath; } private PathSegment createNext() { if (remainingPath == null || remainingPath.length() == 0) { return null; } int dot = remainingPath.indexOf(pathSeparator); String pre = dot > 0 ? remainingPath.substring(0, dot) : remainingPath; String post = dot > 0 ? remainingPath.substring(dot + 1) : ""; return createChild(pre, post); } protected PathSegment createChild(String incomingPath, String remainingPath) { JsonNode child = node != null ? node.get(incomingPath) : null; return new PathSegment(child, this, incomingPath, remainingPath, pathSeparator); } void fillMissingParents() { parent.fillMissingNodes(incomingPath, node, new JsonNodeFactory(true)); } void fillMissingNodes(String field, JsonNode child, JsonNodeFactory nodeFactory) { if (node == null) { node = new ObjectNode(nodeFactory); parent.fillMissingNodes(incomingPath, node, nodeFactory); } if (child != null) { if (node instanceof ObjectNode) { ((ObjectNode) node).set(field, child); } else { throw new IllegalArgumentException( "Node '" + incomingPath + "' is unexpected in the middle of the path"); } } } @Override public Iterator<PathSegment> iterator() { return new Iterator<PathSegment>() { private PathSegment current = PathSegment.this; private PathSegment next = current.createNext(); @Override public boolean hasNext() { return current != null; } @Override public PathSegment next() { if (!hasNext()) { throw new NoSuchElementException("Past iterator end"); } PathSegment r = current; current = next; next = current != null ? current.createNext() : null; return r; } }; } }